﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using migradorWPF.Base;
using System.Windows.Input;
using System.Windows;
using migradorWPF.Services;
using System.Collections.ObjectModel;
using migradorWPF.Models;
using System.IO;
using System.Configuration;
using System.Threading.Tasks;

namespace migradorWPF.ViewModels
{
    public class MainWindowsViewModel : BaseViewModel
    {
        #region members
        List<String> _basesDeDatosSoportadas = new List<string>();
        private string _baseDeDatosSelected;
        private string _host;
        private string _port;
        private string _nombreBD;
        private string _usuario;
        private string _contrasena;
        private bool _conectado = false;
        private bool _cmbEnabled = true;
        private BDService _baseDeDatos;
        private string _colorDisabled = "White";
        private string _pathDirParches;
        private ObservableCollection<ArchivoMigracion> _parches;
        private string _logSalidaPantalla;
        private ObservableCollection<ParcheAplicado> _parchesAplicados;
        private bool _procedimientosAlmacenados;
        private string _mensajesAplicacion;
        #endregion
        
        #region properties
        public List<String> BasesDeDatosSoportadas
        {
            get { return _basesDeDatosSoportadas; }
            set { _basesDeDatosSoportadas = value; }
        }

        public ICommand ConnectDataBaseCommand
        {
            get;
            set;
        }

        public ICommand DesconectarCommand
        {
            get;
            set;
        }

        public ICommand AplicarParchesCommand
        {
            get;
            set;
        }

        public ICommand CancelarAplicarParchesCommand
        {
            get;
            set;
        }

        public string BaseDeDatosSelected
        {
            get { return _baseDeDatosSelected; }
            set { _baseDeDatosSelected = value; }
        }

        public string Host
        {
            get { return _host; }
            set { _host = value; }
        }

        public string Port
        {
            get { return _port; }
            set { _port = value; }
        }

        public string NombreBD
        {
            get { return _nombreBD; }
            set { _nombreBD = value; }
        }

        public string Usuario
        {
            get { return _usuario; }
            set { _usuario = value; }
        }

        public string Contrasena
        {
            get { return _contrasena; }
            set { _contrasena = value; }
        }

        public bool Conectado
        {
            get { return _conectado; }
            set { 
                _conectado = value;
                this.CmbEnabled = !_conectado;
                if (_conectado)
                {
                    this.ColorDisabled = "LightGray";                    
                }
                else
                {
                    this.ColorDisabled = "White";
                }
                RaisePropertyChanged("Conectado");
            }
        }

        public BDService BaseDeDatos
        {
            get { return _baseDeDatos; }
            set { _baseDeDatos = value; }
        }

        public string ColorDisabled
        {
            get { return _colorDisabled; }
            set { 
                _colorDisabled = value;
                RaisePropertyChanged("ColorDisabled");
            }
        }

        public bool CmbEnabled
        {
            get { return _cmbEnabled; }
            set { 
                _cmbEnabled = value;
                RaisePropertyChanged("CmbEnabled");
            }
        }

        public string PathDirParches
        {
            get { return _pathDirParches; }
            set { _pathDirParches = value; }
        }

        public ObservableCollection<ArchivoMigracion> Parches
        {
            get { return _parches; }
            set { 
                _parches = value;
                RaisePropertyChanged("Parches");
            }
        }

        public string LogSalidaPantalla
        {
            get { return _logSalidaPantalla; }
            set { 
                _logSalidaPantalla = value;
                RaisePropertyChanged("LogSalidaPantalla");
            }
        }

        public ObservableCollection<ParcheAplicado> ParchesAplicados
        {
            get { return _parchesAplicados; }
            set { 
                _parchesAplicados = value;
                RaisePropertyChanged("ParchesAplicados");
            }
        }

        public bool ProcedimientosAlmacenados
        {
            get { return _procedimientosAlmacenados; }
            set { 
                _procedimientosAlmacenados = value;
                if (BaseDeDatos != null)
                {
                    BaseDeDatos.ProcedimientosAlmacenados = value;
                }
                RaisePropertyChanged("ProcedimientosAlmacenados");
            }
        }

        public string MensajesAplicacion
        {
            get { return _mensajesAplicacion; }
            set { 
                _mensajesAplicacion = value;
                RaisePropertyChanged("MensajesAplicacion");
            }
        }
        #endregion

        public MainWindowsViewModel()
        {
            this.ConnectDataBaseCommand = new DelegateCommand(this.ConnectDataBaseAction);
            this.DesconectarCommand = new DelegateCommand(this.DesconectarBDAction);
            this.AplicarParchesCommand = new DelegateCommand(this.AplicarParchesAction);
            CancelarAplicarParchesCommand = new DelegateCommand(CancelarAplicarParchesAction);
            this.Parches = new ObservableCollection<ArchivoMigracion>();

            // combo bases de datos            
            BasesDeDatosSoportadas.Add(Constantes.BasesDeDatosSoportadas.SQL_SERVER);
            BasesDeDatosSoportadas.Add(Constantes.BasesDeDatosSoportadas.POSTGRESQL);
            BasesDeDatosSoportadas.Add(Constantes.BasesDeDatosSoportadas.Oracle10g11g);
            BasesDeDatosSoportadas.Add(Constantes.BasesDeDatosSoportadas.Mysql5x);
            this.BaseDeDatosSelected = BasesDeDatosSoportadas.FirstOrDefault();
            
            LeerParametrosDeConexion();
        }

        /// <summary>
        /// establece la conexion a la base de datos
        /// </summary>
        /// <param name="o"></param>
        public void ConnectDataBaseAction(Object o)
        {
            if (Conectado)
            {
                MessageBox.Show("Ya existe una conexión abierta");
                return;
            }

            this.BaseDeDatos = BDService.getDbInstance(this.BaseDeDatosSelected);

            this.BaseDeDatos.ProcedimientosAlmacenados = ProcedimientosAlmacenados;
            this.BaseDeDatos.TypeDb = this.BaseDeDatosSelected;
            this.BaseDeDatos.Host = this.Host;
            this.BaseDeDatos.Port = this.Port;
            this.BaseDeDatos.NombreDb = this.NombreBD;
            this.BaseDeDatos.UserDb = this.Usuario;
            this.BaseDeDatos.PassUserDb = this.Contrasena;

            bool coneccion = this.BaseDeDatos.Conectar();
            ParchesAplicados = new ObservableCollection<ParcheAplicado>();
            Parches = new ObservableCollection<ArchivoMigracion>();

            if (coneccion)
            {
                Conectado = true;
                GuardarParametrosDeConexion();
                MessageBox.Show("Conección exitosa");
            }
            else
            {
                LogSalidaPantalla += "\n" + BaseDeDatos.ErrorBaseDeDatos;
                MessageBox.Show("Error de conección");
                Conectado = false;
            }
        }

        /// <summary>
        /// guarda los datos de conexion en el app.config
        /// </summary>
        public void GuardarParametrosDeConexion()
        {
            try
            {
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

                config.AppSettings.Settings["TypeDb"].Value = BaseDeDatosSelected;
                config.Save(ConfigurationSaveMode.Modified);

                config.AppSettings.Settings["Host"].Value = Host;
                config.Save(ConfigurationSaveMode.Modified);

                config.AppSettings.Settings["Port"].Value = Port;
                config.Save(ConfigurationSaveMode.Modified);

                config.AppSettings.Settings["NombreDb"].Value = NombreBD;
                config.Save(ConfigurationSaveMode.Modified);

                config.AppSettings.Settings["UserDb"].Value = Usuario;
                config.Save(ConfigurationSaveMode.Modified);

                config.AppSettings.Settings["PassUserDb"].Value = Contrasena;
                config.Save(ConfigurationSaveMode.Modified);
            }
            catch (Exception ex)
            {
                //none
                Logger.log(ex.Message, Logger.Level.ERROR);
            }
        }

        public void LeerParametrosDeConexion()
        {
            try
            {
                string baseDeDatos = ConfigurationManager.AppSettings["TypeDb"];
                BaseDeDatosSelected = BasesDeDatosSoportadas.Find(a => a == baseDeDatos);
                if (String.IsNullOrEmpty(BaseDeDatosSelected))
                    BaseDeDatosSelected = BasesDeDatosSoportadas.FirstOrDefault();

                Host = ConfigurationManager.AppSettings["Host"];
                Port = ConfigurationManager.AppSettings["Port"];
                NombreBD = ConfigurationManager.AppSettings["NombreDb"];
                Contrasena = ConfigurationManager.AppSettings["PassUserDb"];
                Usuario = ConfigurationManager.AppSettings["UserDb"];

            }
            catch (Exception ex)
            {
                //none
                Logger.log(ex.Message, Logger.Level.ERROR);
            }
        }

        /// <summary>
        /// desconecta de la conexion actual a la base de datos
        /// </summary>
        /// <param name="o"></param>
        public void DesconectarBDAction(Object o)
        {
            BaseDeDatos.Desconectar();
            Conectado = false;

            //limpiamos variables
            Parches = new ObservableCollection<ArchivoMigracion>();
            ParchesAplicados = new ObservableCollection<ParcheAplicado>();
        }

        public async void AplicarParchesAction(Object o)
        {
            if (!Conectado)
            {
                MessageBox.Show("No existe una conexión activa");
                return;
            }

            if (BaseDeDatos.AplicandoParches)
            {
                MessageBox.Show("Se esta ejecutando una operación actualmente");
                return;
            }

            if (Parches.Count == 0)
            {
                MessageBox.Show("No existen parches para aplicar");
                return;
            }

            if (!BaseDeDatos.ExisteConexion())
            {
                MessageBox.Show(Constantes.Mensages.ERROR_NO_EXISTE_CONEXION);
                return;
            }
            
            if (!BaseDeDatos.ExisteTablaParches())
            {
                if (!BaseDeDatos.CrearTablaParches())
                {
                    MessageBox.Show(Constantes.Mensages.ERROR_NO_CREACION_TABLA_PARCHES);
                    return;
                }
            }

            // se aplican los parches
            await Task.Run(() =>
            {
                MensajesAplicacion = "Aplicando parches...";
                BaseDeDatos.AplicarParches(Parches);
                LogSalidaPantalla = BaseDeDatos.ErrorBaseDeDatos;
            });

            LeerDirectorioParches();
            MensajesAplicacion = String.Empty;
        }

        public void CancelarAplicarParchesAction(Object o)
        {
            if (BaseDeDatos != null)
            {
                BaseDeDatos.CancelarAplicarParches();
            }
        }

        public void LeerDirectorioParches()
        {
            ParchesAplicados = new ObservableCollection<ParcheAplicado>();
            if (!BaseDeDatos.ObtenerParchesAplicados())
            {
                return;
            }

            ParchesAplicados = BaseDeDatos.ParchesAplicados;

            DirectoryInfo directorioParches = new DirectoryInfo(this.PathDirParches);
            FileInfo[] archivosDirParches = directorioParches.GetFiles("*.sql");
            List<String> parchesNoAplicados = new List<String>();
            this.Parches = new ObservableCollection<ArchivoMigracion>();

            foreach (var parche in archivosDirParches)
            {
                // si el parche esta aplicado se omite
                if ((BaseDeDatos.ParchesAplicados != null) && (BaseDeDatos.ParchesAplicados.FirstOrDefault(a => a.Nombre_parche == parche.Name) != null))
                {
                    //si son procedimientos almacenados no se omite
                    if(!ProcedimientosAlmacenados)
                        continue;
                }
                this.Parches.Add(new ArchivoMigracion() { 
                    Nombre = parche.Name,
                    Tamanio = parche.Length,
                    UltimaModificacion = parche.LastWriteTime,
                    File = parche
                });
                
            }

            if (this.Parches.Count == 0)
            {
                LogSalidaPantalla += "\nNo se encontraron parches para aplicar";
            }
        }

        public void BorrarDatosParches()
        {
            Parches = new ObservableCollection<ArchivoMigracion>();
            ParchesAplicados = new ObservableCollection<ParcheAplicado>();
        }
    }
}
